package cn.serein.charging.order.service.impl;

import cn.serein.charging.common.pojo.JsonResult;
import cn.serein.charging.common.pojo.param.GunStatusUpdateParam;
import cn.serein.charging.common.pojo.vo.StationInfoVO;
import cn.serein.charging.common.pojo.vo.UserInfoVO;
import cn.serein.charging.common.utils.JsonUtils;
import cn.serein.charging.order.constant.Constants;
import cn.serein.charging.order.constant.MqttConstant;
import cn.serein.charging.order.feign.DeviceClient;
import cn.serein.charging.order.feign.UserClient;
import cn.serein.charging.order.mqtt.MqttProducer;
import cn.serein.charging.order.pojo.dto.ChargingDto;
import cn.serein.charging.order.pojo.dto.StationInfoDto;
import cn.serein.charging.order.pojo.dto.UserInfoDto;
import cn.serein.charging.order.pojo.param.OrderAddParam;
import cn.serein.charging.order.pojo.po.OrderMQPO;
import cn.serein.charging.order.quartz.DeviceCheckJob;
import cn.serein.charging.order.rabbit.RabbitMQOrderProducer;
import cn.serein.charging.order.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.SchedulerException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 创建订单的流程
 * 1、生成订单号
 * 2、获取场站的信息和充电桩信息
 * 3、获取用户车辆信息
 * 4、给设备发送充电指令
 * 5、修改充电桩的状态为正在使用
 */
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @Override
    public String createOrder(OrderAddParam orderAddParam) {

        // 订单号
        String orderNo = getOrderNo();

        // 获取场站信息

        StationInfoDto stationInfoDto = getStationInfoByGunId(orderAddParam.getGunId());

        // 获取用户车辆信息
        UserInfoDto userInfoDto = getUserInfoByUserId(orderAddParam.getUserId());

        // 修改充电桩的状态为正在使用
        Boolean success = updateGunStatusBusy(orderAddParam.getGunId());

        // 给设备发送充电指令之前  需要添加一个计时任务
        try {
            DeviceCheckJob deviceCheckJob = new DeviceCheckJob(orderNo,orderAddParam.getGunId());
        } catch (SchedulerException e) {
            //可以抛一个业务异常,通过全局异常处理器处理,
            //告诉用户 给用户提示信息 订单创建失败,不要提示具体代码信息异常信息
            //
            log.error("设备自检任务创建失败");
        }

        // 给设备发送充电指令
        startCharging(orderNo,orderAddParam.getPileId(), orderAddParam.getGunId());


        // 给RabbitMQ发送延迟消息
        sendOrderMessage(orderNo);

        return orderNo;
    }

    @Autowired
    private RabbitMQOrderProducer rabbitMQOrderProducer;

    private void sendOrderMessage(String orderNo){
        OrderMQPO orderMQPO = new OrderMQPO();
        orderMQPO.setOrderNo(orderNo);

        rabbitMQOrderProducer.sendOrder(orderMQPO);
    }


    /**
     * 修改充电桩的状态为正在使用  需要使用deviceClient
     */
    @Autowired
    private DeviceClient deviceClient;

    private Boolean updateGunStatusBusy(Integer gunId) {

        GunStatusUpdateParam param = new GunStatusUpdateParam();

        param.setGunId(gunId);
        // 设置状态，但是这里一般不使用具体的数字，而是通过枚举来表示   魔数
        param.setStatus(Constants.GUN_STATUS_BUSY);

        JsonResult<Boolean> jsonResult = deviceClient.updateGunStatus(param);
        if(jsonResult != null){
            return jsonResult.getData();
        }
        return false;
    }

    @Autowired
    private MqttProducer mqttProducer;
    private void startCharging(String orderNo, Integer pileId, Integer gunId) {

        // 格式： topic话题前缀 + 充电桩id   不需要加枪的id
        //  设备去订阅这个话题，拿到消息后，
        String topic = MqttConstant.TOPIC_START_CHARGING_PREFIX + "/" + pileId;
        // 创建ChargingDto对象，封装订单号，用户车辆信息，场站信息
        ChargingDto chargingDto = new ChargingDto();
        chargingDto.setOrderNo(orderNo);
        chargingDto.setPileId(pileId);
        chargingDto.setGunId(gunId);
        chargingDto.setMessage(Constants.START_CHARGING);
        // 这里需要将对象转化为字符串    需要引入依赖    和创建工具类
        String json = JsonUtils.toJson(chargingDto);
        log.debug("订单服务发送开始充电指令到设备topic:{},消息:{}",topic,json);

        // 发送消息
        Boolean success = mqttProducer.send(topic,json);
        log.debug("订单服务发送开始充电指令到设备topic:{},消息:{},结果: {}",topic,json,success);

    }

    /**
     * 根据用户id获取用户车辆信息  需要使用userClient
     * @param userId
     * @return
     */
    @Autowired
    private UserClient userClient;

    private UserInfoDto getUserInfoByUserId(Integer userId) {

        // 调用用户服务层的获取用户的车辆信息
        JsonResult<UserInfoVO> userCarInfo = userClient.getUserInfo(userId);

        // 如果车辆信息不为空
        if(userCarInfo != null){
            // 将vo转为dto实现服务之间的通信
            UserInfoDto userInfoDto = new UserInfoDto();
            userInfoDto.setCarId(userCarInfo.getData().getCarId());
            return userInfoDto;
        }

        return null;
    }

    /**
     * 根据枪id获取场站信息
     * @param gunId
     * @return
     */
    private StationInfoDto getStationInfoByGunId(Integer gunId) {
        JsonResult<StationInfoVO> station = deviceClient.getStationInfo(gunId);

        if(station != null){
            StationInfoVO data = station.getData();

            if(data != null){
                // 将vo转为dto用于服务之间的传递
                StationInfoDto stationInfoDto = new StationInfoDto();
                // 使用BeanUtils工具类将vo转为dto
                // todo copyProperties 底层实现
                BeanUtils.copyProperties(stationInfoDto,data);

                return stationInfoDto;
            }

        }


        return null;
    }

    private String getOrderNo() {
        return "126_55";
    }
}
